﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace InnovTech.Util.Reflection
{
    public static class ReflectionUtil
    {
        /// <summary>
        /// Gets the <see cref="FieldInfo"/> for the given <see cref="Type"/> and the field name.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static FieldInfo GetField(Type type, string fieldName)
        {
            return GetMember<FieldInfo>(type, fieldName);
        }

        /// <summary>
        /// Gets the <see cref="PropertyInfo"/> for the given <see cref="Type"/> and the property name.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static PropertyInfo GetProperty(Type type, string propertyName)
        {
            return GetMember<PropertyInfo>(type, propertyName);
        }

        /// <summary>
        /// Gets the property value for the given instance and property name
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="instance"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static T GetPropertyValue<T>(object instance, string propertyName)
        {
            return (T)GetProperty(instance.GetType(), propertyName).GetValue(instance, null);
        }
        /// <summary>
        /// Gets the field value for the given instance and the field name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="instance"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static T GetFieldValue<T>(object instance, string fieldName)
        {
            return (T)GetField(instance.GetType(), fieldName).GetValue(instance);
        }

        /// <summary>
        /// Gets a member of the given <see cref="Type"/> und name. 
        /// This method searches through the inheritance tree, and even the private inherited members will be found out.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="memberName"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public static T GetMember<T>(Type type, string memberName, BindingFlags flags) where T : MemberInfo
        {
            foreach (var member in type.GetMembers(flags))
            {
                // Ignore inherited fields.
                if (member.Name == memberName)
                    return member as T;
            }

            var baseType = type.BaseType;
            if (baseType != null)
                return GetMember<T>(baseType, memberName, flags);

            return null;
        }

        /// <summary>
        /// Gets a member of the given <see cref="Type"/> und name using a standard <see cref="BindingFlags"/>: BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance.
        /// This method searches through the inheritance tree, and even the private inherited members will be found out.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static T GetMember<T>(Type type, string memberName) where T : MemberInfo
        {
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            return GetMember<T>(type, memberName, flags);
        }

        /// <summary>
        /// Gets the static member for the given <see cref="Type"/> and member name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static T GetStaticMember<T>(Type type, string memberName) where T : MemberInfo
        {
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
            return GetMember<T>(type, memberName, flags);
        }

        /// <summary>
        /// Gets the static property value for the given <see cref="Type"/> and property name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static T GetStaticPropertyValue<T>(Type type, string propertyName)
        {
            return (T)GetStaticMember<PropertyInfo>(type, propertyName).GetValue(null, null);
        }

        /// <summary>
        /// Gets the static field value for the given <see cref="Type"/> and field name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static T GetStaticFieldValue<T>(Type type, string fieldName)
        {
            return (T)GetStaticMember<FieldInfo>(type, fieldName).GetValue(null);
        }
    }
}
